#ifndef TOOLS_H
#define TOOLS_H
#include "myinet.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <signal.h>


/** 打印错误并且结束程序
 *
 * @param errorcmd 出错的命令的名称，如socket
 * @param errormsg 需要显示的出错信息
 *
 * errorcmd和errormsg都可以为NULL，当为NULL时不打印相关信息，例如当errormsg为
 * NULL，则不打印额外的信息，errorcmd为NULL时，不打印perror显示的信息
 */
void error_exit(const char* errorcmd, const char* errormsg);
// 一些用于网络编程的工具

// some log functions
/// debug 日志
/**
 * tools里面有关于打印相关信息的函数，如下
 * - debug
 * - info
 * - warn
 * - error
 *
 * @note 它们都将信息输出到stderr
 * 
 * 使用方法和printf类似，示例如下:
 * ```c
 * debug("%d plus %d equals %d\n", 1, 2, 3);
 * ```
 */
void debug(const char* fmt, ...);

/// 见\ref debug
void info(const char* fmt, ...);
/// 见\ref debug()
void warn(const char* fmt, ...);
/// 见\ref ::debug()
void error(const char* fmt, ...);


/** 用于控制终端输出颜色的函数
 *
 * 在终端中的字有三种属性：前景色、背景色和其它显示方式（默认、高亮、下划线）
 * 所以设置了三个参数，可以一次设置3个参数，当然，如果只想设置一个，其它传入
 * null即可
 *
 * 在设置之后输出到stderr的字符就会有相应效果，如果有同属性的参数输入，比如黄色和红色，那么以参数表中较后的参数为准
 *
 * 设置颜色之后会一直有效，除非显式地调用\ref reset_term_color 重置
 *
 */
void set_term_color(const char* color, const char* color2, const char* color3);

/// 使用这个来清除输出字体的各种属性
void reset_term_color();


/**
 * @brief 创建一个被动socket（已经对其调用listen）
 * @param portNumber port number on which the server is running
 * @return on success, return the sockfd. Otherwise return a negative number
 */
int getTcpServerSocket(unsigned short portNumber);

/**
 * @brief 获取一个客户端socket，获取之后调用connect连接
 * create a client socket and connect
 * @param server server address, can be domain or ip address
 * @return on success, return the sockfd. Otherwise negative
 */
int getTcpClientSocket(const char* server, int serverPort);

/**
 * 获取一个被动的socket
 * @param portNumber 网络字节序
 * @param type socket调用里面的type
 * @param qlen 连接队列的长度, 如果type是SOCK_DGRAM，那么此参数不起作用
 * @return sockfd或者负数 
 */
int get_server_socket(int portNumber, int type, int qlen);

/*! \brief 连接到远端服务器
 *
 * Example: 
 * ```cpp
 * struct in_addr sin_addr;
 * get_in_addr_by_name_or_ip("localhost", &sin_addr);
 * connectTo(sin_addr, 8080, SOCK_STREAM)
 * ```
 *
 * 没有使用const char* 类型的host是因为在程序内部传递非字符串类型比较方便，
 * 不需要转换
 * @param sin_addr ip地址
 * @param portNumber 网络字节序
 * @param type 同上
 * @return sockfd或者负数
 */
int connectTo(struct in_addr sin_addr, int portNumber, int type);

/** 构建一个struct sockaddr_in结构体
 * @param portNumber 同样是主机字节序
 * @param addr ip地址，网络字节序，用于对sockaddr_in内部的sin_addr赋值
 */
struct sockaddr_in get_sockaddr_in(int portNumber, struct in_addr sin_addr);

/** 获取struct in_addr
 * @param[in] name_or_ip host name like 'baidu.com' or ip address like '192.168.1.1'
 * @param[out] 输出的网络地址
 * @return 0 for **success**, -1 for **failure**
 *
 * 获取域名或者ip对应的in_addr结构体
 */
int get_in_addr_by_name_or_ip(const char* name_or_ip, struct in_addr* sin_addr);


/**
 * 收到SIGINT时调用exit，这样会把所有的文件描述符都关闭
 */
void register_safe_exit_on_sigint();
void safe_exit(int n);


#endif // TOOLS_H

